package cn.levi.dict.configuration;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.levi.dict.DictToolContextHolder;
import cn.levi.dict.annotation.DictFormat;
import cn.levi.dict.annotation.DictFormat.DefaultEnum;
import cn.levi.dict.enums.BaseEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Optional;

/**
 * 字典自定义序列化规则
 */
public class DictSerializer extends JsonSerializer<Integer> implements ContextualSerializer {

	/**
	 * 生成序列化字段后缀
	 */
	private static final String LABEL_SUFFIX = "Cn";
	/**
	 * 字典配置信息
	 */
	private DictFormat dictFormat;

	/**
	 * 根据code获取枚举的描述的方法
	 */
	private static final String[] METHOD = SpringUtil.getBean(DictFormatProperties.class).getEnumDescMethodNames();

	public DictSerializer(DictFormat enumFormat) {
		this.dictFormat = enumFormat;
	}

	public DictSerializer() {
	}


	@Override
	public void serialize(Integer value, JsonGenerator gen, SerializerProvider provider) throws IOException {
		provider.defaultSerializeValue(value, gen);
		// 添加转换之后的字段
		String fieldName = gen.getOutputContext().getCurrentName();
		String targetedFieldName = dictFormat.targetFieldName();
		if (StrUtil.isBlank(targetedFieldName)) {
			gen.writeObjectField(fieldName.concat(LABEL_SUFFIX), routerCovertHandler(dictFormat, value));
			return;
		}
		gen.writeObjectField(targetedFieldName, routerCovertHandler(dictFormat, value));
	}

	@Override
	public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
		DictFormat dictFormatTarget = property.getAnnotation(DictFormat.class);
		if (dictFormatTarget == null) {
			return prov.findValueSerializer(property.getType(), property);
		}
		return new DictSerializer(dictFormatTarget);
	}

	/**
	 * 路由到具体的处理器
	 *
	 * @param dictFormat dict格式参数
	 * @param value      值
	 * @return
	 */
	private Object routerCovertHandler(DictFormat dictFormat, Object value) {
		try {
			Class<? extends Enum<?>> en = dictFormat.value();
			//优先从枚举中获取,如果枚举中没有,则从字典中获取
			if (!DefaultEnum.class.equals(en)) {
				Object enumDesc = getEnumDesc(dictFormat, value);
				if (enumDesc != null) {
					return enumDesc;
				}
			}
			Object dictDesc = getDictDesc(dictFormat, value);
			if (dictDesc != null) {
				return dictDesc;
			}
			return dictFormat.defaultValue();
		} catch (Exception e) {
			return dictFormat.defaultValue();
		}

	}

	/**
	 * 获取字典的转换值
	 *
	 * @param dictFormat dict格式参数
	 * @param value      值
	 * @return
	 */
	private Object getDictDesc(DictFormat dictFormat, Object value) {
		String typeCode = dictFormat.typeCode();
		if (StrUtil.isBlank(typeCode)||value==null) {
			return dictFormat.defaultValue();
		}
		String dict = getDictToolContextHolder().getDict(typeCode, String.valueOf(value));
		return StrUtil.isNotBlank(dict) ? dict : dictFormat.defaultValue();
	}

	private  DictToolContextHolder getDictToolContextHolder() {
		return SpringUtil.getBean(DictToolContextHolder.class);
	}
	/**
	 * 获取枚举类型的描述信息
	 *
	 * @param dictFormat  dict格式参数
	 * @param value 值
	 * @return 枚举desc字段
	 */
	private Object getEnumDesc(DictFormat dictFormat, Object value) {
		if (dictFormat == null || value == null) {
			return null;
		}
		Class<? extends Enum<?>> et = dictFormat.value();
		if (DefaultEnum.class.equals(et)) {
			return null;
		}
		//如果继承了BaseEnum 则直接获取name
		if (BaseEnum.class.isAssignableFrom(et)) {
			return  getNameByCodeFromBaseEnum(et, value);
		}
		return getEnuName(value, et);
	}
	/**
	 * 从 BaseEnum 接口获取名称
	 */
	@SuppressWarnings("unchecked")
	private String getNameByCodeFromBaseEnum(Class<?> enumClass, Object value) {
		try {
			// 使用反射调用 BaseEnum 的静态方法，避免类型安全问题
			Method getNameByCode = ReflectUtil.getMethodByName(BaseEnum.class,true ,"getNameByCode");
			Object object = ReflectUtil.invokeStatic(getNameByCode, enumClass,value);
			return Optional.ofNullable(object).map(Object::toString).orElse(null);
		} catch (Exception e) {
			//不印象原有逻辑，不做处理
			return null;
		}
	}

	/**
	 * 获取枚举的name
	 *
	 * @param value code
	 * @param et    et
	 * @return
	 */
	public static Object getEnuName(Object value, Class<? extends Enum<?>> et) {
		//根据code 获取枚举描述的方法 获取枚举的方法
		Method method = null;
		for (String name : METHOD) {
			 method = ReflectUtil.getMethod(et, name, Integer.class);
			 if (method!=null)
			 {
				 break;
			 }
		}
		if (method==null)
		{
			throw new RuntimeException(StrUtil.format("枚举：{}未提供获取对应描述的方法，请提供方法：{}",et.getName(),METHOD));
		}
		method.setAccessible(true);
		return ReflectUtil.invokeStatic(method, value);
	}
}
